package com.ophelisis.common.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Stack;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;

/**
 * 선택한 파일을 지정한 이름으로 압축하기
 * @author hwanLee
 *
 */
public class CompressionUtil {
	private static boolean debug = true;
	/**
	 * compresses the given file(or dir) and creates new file under the same directory.
	 * @param src file or directory
	 * @throws IOException
	 */
	@SuppressWarnings("rawtypes")
	public void zip(String fileName, File src, List fileList) throws IOException{
		zip(fileName, src, fileList, Charset.defaultCharset().name(), true);
	}
	
	/**
	 * zips the given file(or dir) and create 
	 * @param src file or directory to compress
	 * @param includeSrc if true and src is directory, then src is not included in the compression. if false, src is included.
	 * @throws IOException
	 */
	@SuppressWarnings("rawtypes")
	public void zip(String fileName, File src, List fileList, boolean includeSrc) throws IOException{
		zip(fileName, src, fileList, Charset.defaultCharset().name(), includeSrc);
	}
	
	/**
	 * compresses the given src file (or directory) with the given encoding
	 * @param src
	 * @param charSetName
	 * @param includeSrc
	 * @throws IOException
	 */
	@SuppressWarnings("rawtypes")
	public void zip(String fileName, File src, List fileList, String charSetName, boolean includeSrc) throws IOException {
		zip(fileName, src, src.getParentFile(), fileList, charSetName, includeSrc);
	}
	
	/**
	 * compresses the given src file(or directory) and create the compressed file under the given destDir. 
	 * @param src
	 * @param destDir
	 * @param charSetName
	 * @param includeSrc
	 * @throws IOException
	 */
	@SuppressWarnings("rawtypes")
	public void zip(String fileName, File src, File destDir, List fileList, String charSetName, boolean includeSrc) throws IOException {
		if (!src.isDirectory()){
			int pos = fileName.lastIndexOf(".");
			if (pos >  0){
				fileName = fileName.substring(0, pos);
			}
		}
		fileName += ".zip";
		File zippedFile = new File (destDir, fileName);
		if (!zippedFile.exists()) zippedFile.createNewFile();
		zip(src, new FileOutputStream(zippedFile), fileList, charSetName, includeSrc);
	}
	
	@SuppressWarnings("rawtypes")
	public void zip(File src, OutputStream os, List fileList, String charsetName, boolean includeSrc)
	throws IOException {
		System.out.println(">>start src :: " + src);
		ZipArchiveOutputStream zos = new ZipArchiveOutputStream(os);
		zos.setEncoding(charsetName);
		FileInputStream fis ;
		
		int length ;
		ZipArchiveEntry ze ;
		byte [] buf = new byte[8 * 1024];
		String name ;
		
		Stack<File> stack = new Stack<File>();
		File root;
		debug(">>src.isDirectory() :: " + src.isDirectory() + " // includeSrc :: " + includeSrc);

		stack.push(src);
		root = src;
		
		debug(">>root :: " + root);
		int count = 0;
		while (!stack.isEmpty()){
			File f = stack.pop();
			debug(">>(" + count + ") stack.pop() -> f :: " + f + " (" + f.getAbsolutePath() + ")");
			name = toPath(root, f);
			debug(">>(" + count + ") name :: " + name);
			debug(">>(" + count + ") f.isDirectory() :: " + f.isDirectory());
			if (f.isDirectory()){
				debug (">>(" + count + ") dir :: " + name);
				File [] fs = f.listFiles();
				for (int i = 0; i < fs.length; i++) {
					if (fs[i].isDirectory()){
						stack.push(fs[i]);
					} else {
						if (fileList.contains(new File(fs[i].toString().substring(root.getAbsolutePath().length()).replace(File.separatorChar, '/').replace("/","")))) {
							stack.add(0, fs[i]);	
						}
					}
				}
			} else {
				debug(">>(" + count + ") file :: " + name);
				ze = new ZipArchiveEntry(name);
				zos.putArchiveEntry(ze);
				fis = new FileInputStream(f);
				while ((length = fis.read(buf, 0, buf.length)) >= 0){
					zos.write(buf, 0, length);
				}
				fis.close();
				zos.closeArchiveEntry();
			}
			count++;
		}
		zos.close();
	}
	
	private String toPath(File root, File dir){
		String path = dir.getAbsolutePath();
		path = path.substring(root.getAbsolutePath().length()).replace(File.separatorChar, '/');
		if (path.startsWith("/")) path = path.substring(1);
		if (dir.isDirectory() && !path.endsWith("/")) path += "/" ;
		System.out.println(">>path :: " + path);
		return path ;
	}
	
	private static void debug(String msg){
		if (debug) System.out.println(msg);
	}
}
